#ifndef __STORE_H
#define __STORE_H


#include "../driver/flash.h"
#include "../utils/md5.h"
#include <string.h>

namespace cfg {

template <typename T> struct Box {
    T value;
    uint8_t md5_sum[16];

    bool verify() const {
        uint8_t sum[16];
        utils::md5::sum(&value, sizeof(T), sum);
        return memcmp(sum, md5_sum, 16) == 0;
    }

    void update() { utils::md5::sum(&value, sizeof(T), md5_sum); }
};

template <typename T, uint32_t RAM_ADDR>
class MemStore {

public:
    inline T *operator->() { return &reinterpret_cast<Box<T> *>(RAM_ADDR)->value; }

    bool verify() const {
        return reinterpret_cast<Box<T> *>(RAM_ADDR)->verify();
    }

    void save() {
        reinterpret_cast<Box<T> *>(RAM_ADDR)->update();
    }
};

template <typename T, uint32_t FLASH_ADDR> class Store {

public:
    inline T *operator->() { return &m_box.value; }

    utils::Error load() {
        driver::flash::read(FLASH_ADDR, &m_box, sizeof(m_box));
        bool ret = m_box.verify();
        if (!ret) {
            m_box.value.reset();
            return utils::ec::STORE_SUM_FAIL;
        }
        return utils::ec::OK;
    }

    utils::Error erase() {
        return driver::flash::erase(FLASH_ADDR, sizeof(m_box));
    }

    utils::Error save() {

        utils::Error err = driver::flash::erase(FLASH_ADDR, sizeof(m_box));
        if (err) {
            return err;
        }
        m_box.update();
        err = driver::flash::write(FLASH_ADDR, &m_box, sizeof(m_box));
        return err;
    }

private:
    Box<T> m_box;
};

} // namespace cfg

#endif
